import numpy as np

def loadDataSet(fileName):
    dataSet = []
    labelSet = []
    f = open(fileName, 'r', encoding='utf-8')
    for line in f.readlines():
        lineArr = line.strip().split()
        dataSet.append(list(map(float, lineArr[1:])))
        labelSet.append(float(lineArr[0]))
    f.close()
    return dataSet, labelSet


# x为输入层神经元个数，y为隐层神经元个数，z输出层神经元个数
def paramer_inint(x, y, z):
    # 隐层阈值
    value1 = np.random.randint(-5, 5, (1, y)).astype(np.float64)
    # 输出层阈值
    value2 = np.random.randint(-5, 5, (1, z)).astype(np.float64)
    # 输入层与隐层的连接权重
    weight1 = np.random.randint(-5, 5, (x, y)).astype(np.float64)
    # 隐层与输出层的连接权重
    weight2 = np.random.randint(-5, 5, (y, z)).astype(np.float64)
    return weight1, weight2, value1, value2


def sigmoid(x):
    return 1.0 / (1.0 + np.exp(-x))


def backPropagation(dataSet, labelSet, weight1, weight2, value1, value2):
    # 步长
    lamba = 0.001
    for i in range(len(dataSet)):
        # 输入数据
        inputSet = np.mat(dataSet[i]).astype(np.float64)
        # 数据标签
        outputSet = np.mat(labelSet[i]).astype(np.float64)
        # 隐层输入
        input1 = np.dot(inputSet, weight1).astype(np.float64)
        # 隐层输出
        output2 = sigmoid(input1 - value1).astype(np.float64)
        # 输出层输入
        input2 = np.dot(output2, weight2).astype(np.float64)
        # 输出层输出
        output3 = sigmoid(input2 - value2).astype(np.float64)

        # 更新公式由矩阵运算表示
        a = np.multiply(output3, (1 - output3)) # y(1-y)
        g = np.multiply(a, (outputSet - output3))
        b = np.multiply(output2, (1 - output2)) # b(1-b)
        c = np.dot(g, np.transpose(weight2))
        e = np.multiply(b, c)

        weight1Change = lamba * np.dot(np.transpose(inputSet), e)
        weight2Change = lamba * np.dot(np.transpose(output2), g)
        value1Change = -lamba * e
        value2Change = -lamba * g

        # 更新参数
        weight1 += weight1Change
        weight2 += weight2Change
        value1 += value1Change
        value2 += value2Change
    # print(np.shape(input1))
    return weight1, weight2, value1, value2


def test(dataSet, labelSet, weight1, weight2, value1, value2):
    # 记录预测正确的个数
    rightcount = 0
    for i in range(len(dataSet)):
        # 计算每一个样例通过该神经网路后的预测值
        inputset = np.mat(dataSet[i])
        outputset = np.mat(labelSet[i])
        output2 = sigmoid(np.dot(inputset, weight1) - value1)
        output3 = sigmoid(np.dot(output2, weight2) - value2)

        # 确定其预测标签
        if output3 > 0.5:
            flag = 1
        else:
            flag = 0
        if labelSet[i] == flag:
            rightcount += 1
        # 输出预测结果
        print("预测为%d   实际为%d" % (flag, labelSet[i]))
    # 返回正确率
    return rightcount / len(dataSet)


'''
weight1:输入层与隐层的连接权重
weight2:隐层与输出层的连接权重
value1:隐层阈值
value2:输出层阈值
'''
if __name__ == '__main__':
    fileName = 'horseColicTraining.txt'
    dataSet, labelSet = loadDataSet(fileName)
    N = np.shape(dataSet)[1]
    weight1, weight2, value1, value2 = paramer_inint(N, N, 1)
    for i in range(500):
        weight1, weight2, value1, value2 = backPropagation(dataSet, labelSet, weight1, weight2, value1, value2)
    rate = test(dataSet, labelSet, weight1, weight2, value1, value2)
    print("正确率为%f" % (rate))
